package edu.ccut.saturn.component.impl.node;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import edu.ccut.saturn.component.ComponentManager;
import edu.ccut.saturn.component.IDataMappingRuleInfo;
import edu.ccut.saturn.component.ILogicContext;
import edu.ccut.saturn.component.IParameterContext;
import edu.ccut.saturn.component.IParameterInfo;
import edu.ccut.saturn.component.SaturnData;
import edu.ccut.saturn.component.SaturnDataManager;
import edu.ccut.saturn.component.SaturnDebugThreadsManager;
import edu.ccut.saturn.component.impl.SaturnParameterInfo;
import edu.ccut.saturn.component.loader.node.DataMappingNodeInfoLoader;
import edu.ccut.saturn.component.loader.node.NodeInfoLoader;
import edu.ccut.saturn.component.utils.ExceptionUtils;
import edu.ccut.saturn.component.utils.TypeValidation;
import edu.ccut.saturn.component.xml.ISaturnElement;
import edu.ccut.saturn.component.xml.SaturnElement;

/**
 * 数据映射结点的实现类。在logic组件中调用数据映射结点，表示为数据项SaturnData赋值的操作。
 * 每个结点只能为一个数据项赋值，但这个数据项的字段可以分别从多个不同的数据项实例中取值，也 可以从上下文中的变量取值。
 * <p>
 * 类<code>SaturnDataMappingNodeInfo</code>扩展了<code>AbstractNodeInfo</code>：
 * <li>字段<code>targetValueKey</code>：待赋值的数据项的键值，可以对已经存在的数据项
 * 对象进行赋值，也可以新创建一个数据项对象并对其初始化赋值；
 * <li>字段<code>targetFullKey</code>：记录了定义待赋值的数据项的数据组件的键值；
 * <li>字段<code>rules</code>：记录了映射规则的列表，映射规则描述了数据项中待赋值的字段的赋值来源等信息。
 * 
 * @author 温建
 * 
 * @version 1.0 2008.8.21
 * 
 * @see AbstractNodeInfo
 * @see ILogicContext
 * @see IDataMappingRuleInfo
 * TODO: 日志
 */
public class SaturnDataMappingNodeInfo extends AbstractNodeInfo {

	private String targetValueKey;

	private String targetFullKey;
	// TODO:结点中是不是应该建立对象保存具体细节信息，还是用map等集合保存
	private List<IDataMappingRuleInfo> rules;

	public SaturnDataMappingNodeInfo(String id, String name, String description,
			String nextId, List<IParameterInfo> parameters,
			String targetValueKey, String targetFullKey,
			List<IDataMappingRuleInfo> rules, String x, String y) {

		super(id, name, description, nextId, null, x, y, "saturndata-mapping");
		this.rules = rules;
		this.targetFullKey = targetFullKey;
		this.targetValueKey = targetValueKey;
	}

	/**
	 * 数据映射结点的执行逻辑。 首先获得待赋值的SaturnData类型数据项的实例；如果要给一个还未被创建实例的数据项赋值
	 * 则需要根据数据组件创建数据项实例； 获得待赋值数据项实例后，则遍历映射规则来给数据项的各个字段赋值。
	 * 既可以从其它的数据项的字段赋值也可以从上下文中的简单变量赋值，具体赋值规则在属性<code>List<IDataMappingRuleInfo> rules</code>中记录。
	 * 
	 * TODO：临时变量和对象的get方法 TODO：日志的写法？是否消耗时间和内存地址
	 * 
	 * @param logicContext
	 *            逻辑上下文对象，记录结点运行时的上下文信息
	 * @throws Exception
	 */
	@Override
	public void doExecute(ILogicContext logicContext) throws Exception {
		ComponentManager.beforeExecuteComponent(this, this
				.createParameterContext(logicContext));

		try {

			logger.debug("      node : {execute : '{target-value  : '" + targetValueKey + "'}', process : '" + logicContext.getId() + "'}");
			logger.debug("      node : {execute : '{target-saturn-key  : '" + targetFullKey + "'}', process : '" + logicContext.getId() +"'}");
			if (targetValueKey != null) {
				Object value = logicContext.getValue(targetValueKey);
				SaturnData saturnData = null;
				

				if (value != null) {
					saturnData = (SaturnData) logicContext
							.getValue(targetValueKey);
				}

				if (saturnData == null) {
					saturnData = SaturnDataManager.getInstance().createData(
							targetFullKey);

					logicContext.setValue(targetValueKey, saturnData);
					
				}

				if (this.rules != null) {

					for (IDataMappingRuleInfo rule : rules) {

						if (rule.getSource() != null
								&& !"".equals(rule.getSource())) {

							String key = rule.getKey();
							String itemKey = rule.getValue();

							if (itemKey != null && !itemKey.equals("")) {

								saturnData.set(key, ((SaturnData) logicContext
										.getValue(rule.getSource())).get(rule
										.getValue()));

								logger
										.debug("      node : {execute : 'set target saturn data . target key ["
												+ key
												+ "], source saturn data value key is ["
												+ rule.getSource()
												+ "], source key is ["
												+ rule.getValue()
												+ "], source value is "
												+ ((SaturnData) logicContext
														.getValue(rule
																.getSource()))
														.get(rule.getValue()) + "', process : '" + logicContext.getId() + "'}");

							} else {
								String dataType = saturnData.getSaturnDataInfo()
										.getDataItem(key).getJavaType();

								saturnData.set(key, TypeValidation
										.getValidTypeValue(dataType,
												logicContext.getValue(rule
														.getSource())));

								logger
										.debug("      node : {execute : 'set target saturn data. target key ["
												+ key
												+ "], source key is ["
												+ rule.getSource()
												+ "], source value is ["
												+ logicContext.getValue(rule
														.getSource()) + "', process : '" + logicContext.getId() + "'}");
							}
						}
					}
				}
			}
		} catch (Exception e) {
			logger.error("ERROR : {message : '" + ExceptionUtils.getExMessage(e) + "', process : '" +
				logicContext.getId() + "', node : ' " + super.getNodeType() + " " + super.getId() + " " + super.getName() + "'}");
			throw e;

		} finally {
			ComponentManager.afterExecuteComponent(this, this
					.createParameterContext(logicContext));

		}
	}

	/**
	 * 获得待赋值的数据项的键值
	 * 
	 * @return
	 */
	public String getTargetValueKey() {
		return targetValueKey;
	}

	/**
	 * 设置待赋值的数据项的键值
	 * 
	 * @param targetValueKey
	 */
	public void setTargetValueKey(String targetValueKey) {
		this.targetValueKey = targetValueKey;
	}

	/**
	 * 获得待赋值的数据项的数据组件的键值
	 * 
	 * @return
	 */
	public String getTargetFullKey() {
		return targetFullKey;
	}

	/**
	 * 设置待赋值的数据项的数据组件的键值
	 * 
	 * @param targetFullKey
	 */
	public void setTargetFullKey(String targetFullKey) {
		this.targetFullKey = targetFullKey;
	}

	/**
	 * 获得映射规则信息的列表
	 * 
	 * @return
	 */
	public List<IDataMappingRuleInfo> getRules() {
		return rules;
	}

	/**
	 * 设置映射规则信息的列表
	 * 
	 * @param rules
	 */
	public void setRules(List<IDataMappingRuleInfo> rules) {
		this.rules = rules;
	}

	protected IParameterContext createParameterContext(
			ILogicContext logicContext) throws Exception {

		if (SaturnDebugThreadsManager.getInstance().isStart()) {
			initParameterInfos();
			IParameterContext parameterContext = logicContext
					.createParameterContext(getParameterInfos());

			if (this.getRules() != null) {
				Map<String, Object> mapValue = new HashMap<String, Object>();

				for (IDataMappingRuleInfo ruleInfo : this.getRules()) {
					mapValue.put("targetKey", ruleInfo.getKey());
					mapValue.put("sourceData", ruleInfo.getSource());
					mapValue.put("sourceDataItem", ruleInfo.getValue());
					mapValue.put("sourceFullkey", ruleInfo.getFullKey());
					Object value = null;

					if (ruleInfo.getValue() == null
							|| "".equals(ruleInfo.getValue())) {

						value = logicContext.getValue(ruleInfo.getSource());

					} else {
						value = ((SaturnData) logicContext.getValue(ruleInfo
								.getSource())).get(ruleInfo.getValue());

					}
					mapValue.put("sourceValue", value);
				}
				parameterContext.setParameter(2, mapValue);
			}
			return parameterContext;
		}
		return null;
	}

	private void initParameterInfos() {
		List<IParameterInfo> parameters = new ArrayList<IParameterInfo>();
		SaturnParameterInfo dataParameterInfo = new SaturnParameterInfo();
		dataParameterInfo.setClassType(SaturnData.class);
		dataParameterInfo.setName("target data");
		dataParameterInfo.setId("1");
		dataParameterInfo.setValueKey(this.targetValueKey);
		dataParameterInfo.setScope(IParameterInfo.SCOPE_LOGIC);
		dataParameterInfo.setPassingType(IParameterInfo.PASSING_IN_OUT);
		parameters.add(dataParameterInfo);

		SaturnParameterInfo dataFullKeyParameterInfo = new SaturnParameterInfo();
		dataFullKeyParameterInfo.setClassType(String.class);
		dataFullKeyParameterInfo.setName("target data fullkey");
		dataFullKeyParameterInfo.setId("2");
		dataFullKeyParameterInfo.setValueKey("'" + this.targetFullKey + "'");
		dataFullKeyParameterInfo.setScope(IParameterInfo.SCOPE_LOGIC);
		dataFullKeyParameterInfo.setPassingType(IParameterInfo.PASSING_IN);
		parameters.add(dataFullKeyParameterInfo);

		SaturnParameterInfo dataMappingParameterInfo = new SaturnParameterInfo();
		dataMappingParameterInfo.setClassType(Map.class);
		dataMappingParameterInfo.setName("data mapping infomation");
		dataMappingParameterInfo.setId("3");
		dataMappingParameterInfo.setValueKey("");
		dataMappingParameterInfo.setScope(IParameterInfo.SCOPE_LOGIC);
		dataMappingParameterInfo.setPassingType(IParameterInfo.PASSING_IN_OUT);
		parameters.add(dataMappingParameterInfo);
		super.setParameterInfos(parameters);
	}

	public ISaturnElement toSaturnElement() {
		ISaturnElement dataMappingElement = new SaturnElement(
				DataMappingNodeInfoLoader.KEY_NODE_SATURNDATA_MAPPING);

		dataMappingElement.addAttribute(NodeInfoLoader.KEY_NODE_ID, this
				.getId());

		dataMappingElement.addAttribute(NodeInfoLoader.KEY_NODE_NEXT, this
				.getNextId());

		dataMappingElement.addAttribute(NodeInfoLoader.KEY_NODE_X, this.getX());
		dataMappingElement.addAttribute(NodeInfoLoader.KEY_NODE_Y, this.getY());

		ISaturnElement nameElement = new SaturnElement(
				NodeInfoLoader.KEY_NODE_NAME, this.getName());

		ISaturnElement descriptionElement = new SaturnElement(
				NodeInfoLoader.KEY_NODE_DESCRIPTION, this.getDescription());

		ISaturnElement targetElement = new SaturnElement(
				DataMappingNodeInfoLoader.KEY_NODE_TARGET);

		targetElement.addAttribute(
				DataMappingNodeInfoLoader.KEY_TARGET_VALUE_KEY,
				this.targetValueKey);

		targetElement.addAttribute(
				DataMappingNodeInfoLoader.KEY_TARGET_FULLKEY,
				this.targetFullKey);

		ISaturnElement rulesElement = new SaturnElement(
				DataMappingNodeInfoLoader.KEY_NODE_MAPPING_RULES);

		if (this.rules != null) {

			for (IDataMappingRuleInfo ruleInfo : this.rules) {
				ISaturnElement ruleElement = new SaturnElement(
						DataMappingNodeInfoLoader.KEY_NODE_RULE);

				ruleElement.addAttribute(
						DataMappingNodeInfoLoader.KEY_RULE_KEY, ruleInfo
								.getKey());

				ruleElement.addAttribute(
						DataMappingNodeInfoLoader.KEY_RULE_VALUE, ruleInfo
								.getValue());

				ruleElement.addAttribute(
						DataMappingNodeInfoLoader.KEY_RULE_SOURCE, ruleInfo
								.getSource());

				ruleElement.addAttribute(
						DataMappingNodeInfoLoader.KEY_RULE_FULLKEY, ruleInfo
								.getFullKey());

				rulesElement.addSubElement(ruleElement);
			}
		}
		dataMappingElement.addSubElement(nameElement);
		dataMappingElement.addSubElement(descriptionElement);
		dataMappingElement.addSubElement(targetElement);
		dataMappingElement.addSubElement(rulesElement);
		return dataMappingElement;
	}
}
